Restarting kernel
Please wait...

xxxxxxxxxx


Le data storytelling

Étude de cas : Analyse approfondie des habitudes d'achats d'un groupe de consommateurs


Contexte et objectif

Maintenant que nous avons étudié et mis en forme les données il est temps de passer à l'analyse. Les objectifs d'une analyse exploratoire sont d'obtenir une vision globale d’un jeu de données et découvrir des formes de régularité. Nous allons le faire aux moyens de représentations visuelles (et interactives) des données et de recherche automatique de régularités (corrélation et dépendance entre variables, groupes homogènes, schémas fréquents). Pour chaque sous partie il faut se poser la question de comment bien présenter les résultats et sous quelle forme.

Pour rappel il faut avoir en tête d'expliquer l'activité économique des 20 magasins dont nous disposons dans notre jeu de données. Il est important de se fixer un certain nombre de questions qui vont structurer notre analyse : Comment évolue les ventes dans le temps ? Peut-on déceler des habitudes d'achat ? Quelle est la relation ventes-clients ? Quels sont les magasins les plus vendeurs et les moins vendeurs ? Quels sont ceux qui attirent le plus de clients ou le moins ? Dans les deux cas sont-ils plus ou moins proche de leurs concurrents ? Quels sont les assortiments de produits dans ces magasins qui sont porteurs ? Quel est l'impact des promotions sur les ventes ? Faut-il revoir par conséquent la stratégie sur les promotions ? Quel est l'impact des jours fériés et vacances scolaires sur les ventes ?

Toutes ces questions doivent être en permanence en fil rouge de l'analyse. Pour l'aspect visuel nous privilégierons l'utilisation de seaborn.

Commençons par la phase d'importation des packages

  • Importer les packages pandas, numpy, matplotlib.pyplot, seaborn.
In [1]:
x
 
# Insérez votre code ici
# Import des packages pandas, numpy, matplotlib.pyplot et seaborn
%matplotlib inline
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
In [2]:
 
%matplotlib inline
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
xxxxxxxxxx
  • Importer les données sales_modif.csv dans un DataFrame df.
  • Convertir la variable Date au format datetime à l'aide de la fonction to_datetime de Pandas.
In [3]:
 
# Insérez votre code ici
# Import des données sales_modif.csv dans un DataFrame df
df = pd.read_csv('sales_modif.csv')
# Conversion de la variable 'Date' au format datetime à l'aide de la fonction to_datetime de Pandas
df['Date'] = pd.to_datetime(df['Date'])
# Affichage des 10 premières lignes du DataFrame df
df.head()
Out[3]:
Store Date Sales Customers Promo StateHoliday SchoolHoliday StoreType Assortment CompetitionDistance Cumulative_sales cat_Sales cat_Customers
0 708 2014-01-01 0 0 0 a 1 c c 11470.0 0 (-0.001, 5343.0] (-0.001, 785.0]
1 175 2014-01-01 0 0 0 a 1 c a 4130.0 0 (-0.001, 5343.0] (-0.001, 785.0]
2 242 2014-01-01 0 0 0 a 1 d a 6880.0 0 (-0.001, 5343.0] (-0.001, 785.0]
3 251 2014-01-01 0 0 0 a 1 a c 340.0 0 (-0.001, 5343.0] (-0.001, 785.0]
4 262 2014-01-01 19637 3159 0 a 1 b a 1180.0 19637 (16029.0, 21372.0] (3140.0, 3925.0]
In [4]:
 
df = pd.read_csv('sales_modif.csv')
df['Date'] = pd.to_datetime(df['Date'])
xxxxxxxxxx

Comme pour le module précédent étudions les ventes des 20 magasins par jour. Rappelons que plot_date est l'idéal pour visualiser des valeurs en fonction du temps au format datetime.

  • Recréer le DataFrame sales_per_day qui affiche les ventes et le nombre de clients des magasins par jour.
  • Afficher les ventes par jour à l'aide plot_date de pyplot.

    Exemple d'utilisation : plt.plot_date(x = liste_date, y = valeurs, xdate = True, ls = '-')
In [4]:
 
# Insérez votre code ici
#Comme pour le module précédent étudions les ventes des 20 magasins par jour.
#Rappelons que plot_date est l'idéal pour visualiser des valeurs en fonction du temps au format datetime.
# Recréation du DataFrame sales_per_day qui affiche les ventes et le nombre de clients des magasins par jour
sales_per_day = df.groupby('Date', as_index = False).agg({'Sales':'sum', 'Customers':'sum'})
# Affichage des ventes par jour à l'aide de plot_date de pyplot
sns.set(style = "whitegrid")
plt.figure(figsize = (20, 5))
plt.plot_date(x = sales_per_day['Date'].values, y = sales_per_day['Sales'].values, xdate = True, ls = '-')
plt.title("Evolution des ventes par jour des 20 magasins sur l'année 2014", fontsize = 16)
plt.show()
#Les ventes sur l'année 2014 au global présentent un schéma répétitif avec une poussée des ventes quotidiennes en milieu de semaine.
In [6]:
 
sales_per_day = df.groupby('Date', as_index = False).agg({'Sales':'sum','Customers':'sum'})
sns.set(style="whitegrid")
plt.figure(figsize = (20,5))
plt.title('évolution des ventes sur 2014', fontsize=18)
plt.plot_date(x = sales_per_day['Date'].values, y = sales_per_day['Sales'].values, xdate = True, ls = '-');
xxxxxxxxxx

Les ventes sur l'année 2014 au global présentent un schéma répétitif avec une poussée des ventes quotidiennes en milieu de semaine.

Nous allons également regarder la distribution des variables Sales et Customers à l'aide d'une boîte à moustache. Le recours à ce genre de graphique pour les variables quantitatives est idéal pour visualiser rapidement la distribution d'une variable.

  • Afficher les boxplots de Sales et Customers à l'aide de la fonction boxplot de seaborn.
In [5]:
 
# Insérez votre code ici
#Nous allons également regarder la distribution des variables 'Sales' et 'Customers' à l'aide d'une boîte à moustache.
#Le recours à ce genre de graphique pour les variables quantitatives est idéal pour visualiser rapidement la distribution d'une variable.
# Affichage des boxplots de 'Sales' et 'Customers' à l'aide de la fonction boxplot de seaborn
plt.figure(figsize =(30, 15))
plt.subplot(221)
sns.boxplot(x = 'Sales', data = sales_per_day);
plt.subplot(222)
sns.boxplot(x = 'Customers', data = sales_per_day);
#Les médianes de 'Sales' et 'Customers' se situent respectivement autour de 140 000 et 17 000.
#Les deux présentent les mêmes profils : peu de valeurs au-delà de l'extrémité haute de la boîte à moustache mais un certain nombre à l'extrémité basse.
#Déjà une information, il semblerait que nous ayons quelques magasins qui se démarquent avec des grosses ventes et attirent plus de clients que les autres.
In [6]:
 
plt.figure(figsize = (25,10))
plt.subplot(221)
sns.boxplot(x = 'Sales', data = sales_per_day);
plt.subplot(222)
sns.boxplot(x = 'Customers', data = sales_per_day);
xxxxxxxxxx

Les médianes de Sales et Customers se situent respectivement autour de 140000 et 17000. Les deux présentent les mêmes profils : peu de valeurs au-delà de l'extrémité haute de la boîte à moustache mais un certain nombre à l'extrémité basse. Déjà une information, il semblerait que nous ayons quelques magasins qui se démarquent avec des grosses ventes et attirent plus de clients que les autres.

  • Observer et quantifier la relation linéaire entre les deux variables à l'aide du test de corrélation de pearson.

      Il est conseillé de se resservir de ce qui a été vu dans le module stats exploratoires corrélation.
In [7]:
 
# Insérez votre code ici
### Observation et quantification de la relation linéaire entre les deux variables 'Sales' et 'Customers' à l'aide du test de corrélation de Pearson ###
# Import de la fonction pearsonr du sous-module scipy.stats
from scipy.stats import pearsonr
# Test de corréalation de Pearson entre les deux variables 'Sales' et 'Customers' et affichage du coefficient et de la p-value du test
pd.DataFrame(pearsonr(sales_per_day['Sales'], sales_per_day['Customers']), index = ['pearson_coeff', 'p-value'], columns = ['resultat_test'])
#Le coefficient de pearson est très élevé 0.98. Sans surprise les variables ont une forte relation linéaire.
Out[7]:
resultat_test
pearson_coeff 9.815450e-01
p-value 6.850790e-263
In [8]:
 
from scipy.stats import pearsonr
pd.DataFrame(pearsonr(sales_per_day['Sales'],sales_per_day['Customers']), index = ['pearson_coeff','p-value'], columns = ['resultat_test'])
Out[8]:
resultat_test
pearson_coeff 9.815450e-01
p-value 6.850790e-263
xxxxxxxxxx

Le coefficient de pearson est très élevé 0.98. Sans surprise les variables ont une forte relation linéaire. On peut la vérifier graphiquement. Jointplot est très pratique pour visualiser sur un même graphique la distribution des variables et la relation linéaire entre les deux.

  • Afficher à l'aide de jointplot de seaborn la relation linéaire entre les deux et leur distribution respective.

    Exemple d'utilisation : sns.jointplot(x = col1, y = col2, data = df, kind = 'reg')
In [9]:
 
# Insérez votre code ici
#On peut vérifier graphiquement cette forte relation linéaire entre les variables 'Sales' et 'Customers'.
#Jointplot est très pratique pour visualiser sur un même graphique la distribution des variables et la relation linéaire entre les deux.
# Affichage à l'aide de jointplot de seaborn la relation linéaire entre les deux et leur distribution respective
sns.jointplot(x = 'Customers', y = 'Sales', data = sales_per_day, kind = 'reg');
#La relation linéaire se voit très clairement.
#Remarquons également les distributions particulières de 'Sales' et 'Customers' avec des valeurs basses puis un creux et de nouveau des valeurs.
#Cela fait écho à ce que nous avons vu avec la boxplot, nous avons potentiellement un groupe atypique de magasins avec des chiffres plus faible que les autres.
/home/ubuntu/virtualenvs/python3/lib/python3.6/site-packages/pandas/compat/_optional.py:106: UserWarning: Pandas requires version '2.6.2' or newer of 'numexpr' (version '2.6.1' currently installed).
  warnings.warn(msg, UserWarning)
In [10]:
 
sns.jointplot("Customers", "Sales", data=sales_per_day, kind='reg');
/home/ubuntu/virtualenvs/python3/lib/python3.6/site-packages/seaborn/_decorators.py:43: FutureWarning: Pass the following variables as keyword args: x, y. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation.
  FutureWarning
xxxxxxxxxx

La relation linéaire se voit très clairement. Remarquons également les distributions particulières de Sales et Customers avec des valeurs basses puis un creux et de nouveau des valeurs. Cela fait écho à ce que nous avons vu avec la boxplot nous avons potentiellement un groupe atypique de magasins avec des chiffres plus faible que les autres.

Pour confirmer cette intuition nous allons regarder les ventes par jour de chacun des magasins. L'objectif est de générer 20 graphes en utilisant pour chacun plot_date de la même manière que pour les ventes par jour. Nous devrions observer des courbes atypiques pour certains magasins.

Pour réaliser ces 20 graphiques il faudra générer une figure de taille (40,25) et créer 20 sous figures pour pouvoir y mettre nos graphes. Cela signifie de diviser notre figure en un tableau de 4 lignes et 5 colonnes. L'utilisation de la fonction subplots de pyplot est fortement recommandée. Cette fonction renvoie deux choses : fig qui correspond au cadre dans lequel se trouve les 20 graphes et ax qui est un tableau d'objets. Concrètement ax[0,0] renvoie l'emplacement du premier graphe se trouvant à l'intersection de la première ligne et de la première colonne. Pour chaque emplacement il faut afficher les ventes d'un magasin en particulier, à l'aide plot_date.

Nous vous conseillons de stocker tous les emplacements (ex : (0,0) ou (0,1)) dans une liste coordonnees et d'utiliser une boucle pour afficher vos 20 graphes.

  • Afficher 20 graphes dans une seule figure représentant pour chacun les ventes d'un magasin par jour.
In [11]:
 
# Insérez votre code ici
#Pour confirmer cette intuition nous allons regarder les ventes par jour de chacun des magasins.
#L'objectif est de générer 20 graphes en utilisant pour chacun plot_date de la même manière que pour les ventes par jour,
#afin de vérifier si les 20 magasins suivent la tendance générale.
#Nous devrions observer des courbes atypiques pour certains magasins.
### Affichage de 20 graphes dans une seule figure représentant pour chacun les ventes d'un magasin par jour ###
# Préparation de la figure
liste_magasins = df['Store'].unique()
fig, ax = plt.subplots(4, 5, figsize = (40, 25)) # division de notre figure de taille (40, 25) en un tableau de 4 lignes et 5 colonnes
liste_coordonnees = [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4),
                     (1, 0), (1, 1), (1, 2), (1, 3), (1, 4),
                     (2, 0), (2, 1), (2, 2), (2, 3), (2, 4),
                     (3, 0), (3, 1), (3, 2), (3, 3), (3, 4)]
for i, j in zip(liste_magasins, liste_coordonnees):
    ax[j[0], j[1]].plot_date(x = df[df['Store'] == i]['Date'], y = df[df['Store'] == i]['Sales'], xdate = True, ls = '-')
    
    ax[j[0], j[1]].set_title(str(i), fontsize = 30)
    
plt.show()
#On peut déjà remarquer à l'aide de cette figure que certains magasins se démarquent par la particularité de l'évolution de leurs ventes.
#Le magasin 708 semble avoir un arrêt d'activité entre juillet et septembre voire même octobre.
#A contrario le magasin 262 ne semble pas connaître d'arrêt avec des ventes se situant constamment entre 15000 et 35000€.
#Le magasin 897 semble connaître une poussée dans ses ventes vers la fin de l'année durant le mois de décembre vers la période de Noel.
#Enfin le dernier magasin, le 1021, semble connaître un arrêt d'activité sur la période juillet-août avec de fortes ventes au moment de la reprise.
#A noter que les ordres de grandeur dans les ventes selon les magasins divergent : le magasin 242 a des ventes entre 0 et 8000€ quand le magasin 262 évolue entre 15000 et 35000€.
#Pour les autres dans leur majorité ils ont l'air de suivre le même schéma que la tendance globale.
In [12]:
 
fig, ax = plt.subplots(4,5,figsize = (50,40))
coordonnees = [(0,0),(0,1),(0,2),(0,3),(0,4),(1,0),(1,1),(1,2),(1,3),(1,4),
             (2,0),(2,1),(2,2),(2,3),(2,4),(3,0),(3,1),(3,2),(3,3),(3,4)]
for i,j in zip(coordonnees,df.Store.unique()):
    ax[i[0],i[1]].plot_date(x = df[df.Store == j]['Date'], y = df[df.Store == j]['Sales'], xdate = True, ls = '-');
    ax[i[0],i[1]].set_title(str(j),fontsize=30)
xxxxxxxxxx

L'objectif est de regarder si les 20 magasins suivent la tendance générale. On peut déjà remarquer que certains magasins se démarquent par la particularité de l'évolution de leurs ventes. Le magasin 708 semble avoir un arrêt d'activité entre juillet et septembre voire même octobre. A contrario le magasin 262 ne semble pas connaître d'arrêt avec des ventes se situant constamment entre 15000 et 35000€. Le magasin 897 semble connaître une poussée dans ses ventes vers la fin de l'année durant le mois de décembre vers la période de Noel. Enfin le dernier magasin le 1021 semble connaître un arrêt d'activité sur la période juillet-août avec de fortes ventes au moment de la reprise. A noter que les ordres de grandeur dans les ventes selon les magasins divergent : le magasin 242 a des ventes entre 0 et 8000€ quand le magasin 262 évolue entre 15000 et 35000€.

Pour les autres dans leur majorité ils ont l'air de suivre le même schéma que la tendance globale.

Regardons tout de suite les magasins effectuant le plus de ventes.

Un moyen efficace pour visualiser les ventes par magasin est le recours à un histogramme. En triant les ventes par ordre croissant par magasin on identifie très vite les magasins leader en vente. Nous utiliserons barplot de seaborn. L'attribut order de barplot permet d'ordonner l'histogrammes en lui donnant une liste de valeurs.

Avant d'afficher l'histogramme il faut agréger les données par magasin et les trier. Nous allons utiliser les fonctions groupby et agg pour créer un dataframe agg_store qui contienne les ventes et nombre de clients par magasin. Ensuite nous devons trier agg_store par ordre croissant des ventes en se servant de la fonction sort_values . L'attribut by permet de signifier sur quelle colonne on souhaite trier et ascending (booléen) si on souhaite trier par ordre croissant ou non.

  • Créer agg_store qui regroupe les ventes, le nombre de clients,le nombre de promos (qui nous serviront plus tard) et la distance du magasin concurrent le plus proche, CompetitionDistance, pour chaque magasin.
  • Afficher les ventes effectuées sur l'année par chacun des magasins dans un histogramme.
  • Déterminer et afficher le pourcentage des ventes des 3 plus gros magasins et des 3 plus petits magasins en termes de ventes.

    Exemple d'utilisation : df.groupby(['ma_var'],as_index = False).agg({'var_1':'sum'}]


    Exemple d'utilisation : sns.barplot(x = col1, y = col2, order = liste_valeurs, data = df)
In [23]:
 
# Insérez votre code ici
#Regardons tout de suite les magasins effectuant le plus de ventes.
#Un moyen efficace pour visualiser les ventes par magasin est le recours à un histogramme.
#En triant les ventes par ordre croissant par magasin on identifie très vite les magasins leader en vente.
# Création du DataFrame agg_store qui regroupe les ventes, le nombre de clients, le nombre de promos et 'CompetitionDistance', pour chaque magasin
agg_store = df.groupby(['Store'], as_index = False).agg({'Sales':'sum', 'Customers':'sum', 'Promo':'sum', 'CompetitionDistance':'unique'})
agg_store = agg_store.sort_values(by = 'Sales', ascending = True) # tri par ordre croissant des ventes
hist_color = (0.12156862745098039, 0.47058823529411764, 0.7058823529411765) # couleur bleu foncé sobre, afin que l'histogramme reste professionnel
# Affichage des ventes effectuées sur l'année par chacun des magasins dans un histogramme
plt.figure(figsize= (9, 5))
plt.title("Ventes par magasin", fontsize = 16)
sns.barplot(x = 'Store', y = 'Sales', order = agg_store['Store'].values, data = agg_store, color = hist_color)
plt.show()
# Détermination et affichage du pourcentage des ventes des 3 plus gros magasins et des 3 plus petits magasins en termes de ventes
vente_lead = 0
for i in agg_store['Store'].iloc[-3:]: # -3, -2, et -1 représentent les indices des 3 plus gros magasins en termes de ventes car agg_store est trié p.o.c
    sale = agg_store[agg_store['Store'] == i]['Sales'].sum()
    vente_lead += sale
vente_less = 0
for i in agg_store['Store'].iloc[:3]: # 0, 1, et 2 représentent les indices des 3 plus petits magasins en termes de ventes car agg_store est trié p.o.c
    sale = agg_store[agg_store['Store'] == i]['Sales'].sum()
    vente_less += sale
    
print("Pourcentage des ventes des plus gros magasins : {}%".format(vente_lead / agg_store.Sales.sum()*100))
print("Pourcentage des ventes des plus petits magasins : {}%".format(vente_less / agg_store.Sales.sum()*100))
# Affichage des 10 premières lignes du nouveau DataFrame agg_store
agg_store.head(10)
#Sans grande surprise on retrouve dans les magasins les moins vendeurs le 708 et le 649 ainsi que le 897.
#De même dans les plus vendeurs on retrouve le 262, le 251 et le 788 comme on pouvait s'y attendre avec le graphe précédent.
Pourcentage des ventes des plus gros magasins : 38.38018744668033%
Pourcentage des ventes des plus petits magasins : 6.6862349372546825%
Out[23]:
Store Sales Customers Promo CompetitionDistance
14 897 1002221 107520 140 [3290.0]
8 649 1129865 118328 140 [14570.0]
9 708 1141691 113314 140 [11470.0]
3 242 1225796 109741 140 [6880.0]
0 1 1433408 168764 140 [1270.0]
1 45 1583098 123528 140 [9710.0]
17 953 1631708 164322 140 [19830.0]
12 774 1669990 215764 140 [640.0]
2 175 1780588 212533 140 [4130.0]
18 1015 1856037 149533 140 [9910.0]
In [14]:
 
agg_store = df.groupby(['Store'], as_index = False).agg({'Sales':'sum','Customers':'sum','Promo':'sum','CompetitionDistance':'unique'})
agg_store = agg_store.sort_values(by = 'Sales', ascending = True)
color = (0.12156862745098039, 0.47058823529411764, 0.7058823529411765)
plt.figure(figsize = (9,5))
plt.title('Ventes par magasin',fontsize=18)
sns.barplot(x = 'Store', y = 'Sales', order = agg_store['Store'].values, data = agg_store, color = color);
vente_lead = 0
for i in agg_store['Store'].iloc[-3:]:
    sale = agg_store[agg_store['Store']==i]['Sales'].sum()
    vente_lead += sale
vente_less = 0
for i in agg_store['Store'].iloc[:3]:
    sale = agg_store[agg_store['Store']==i]['Sales'].sum()
    vente_less += sale
print('Pourcentage des ventes des plus gros magasins:',vente_lead/agg_store.Sales.sum()*100,'%')
print('Pourcentage des ventes des plus petits magasins:',vente_less/agg_store.Sales.sum()*100,'%')
Pourcentage des ventes des plus gros magasins: 38.38018744668033 %
Pourcentage des ventes des plus petits magasins: 6.6862349372546825 %
xxxxxxxxxx

Sans grande surprise on retrouve dans les magasins les moins vendeurs le 708 et le 649 ainsi que le 897. De même dans les plus vendeurs on retrouve le 262, le 251 et le 788 comme on pouvait s'y attendre avec le graphe précédent.

On peut aller plus loin en utilisant la variable cat_sales que nous avons créé précédemment. L'objectif est de voir si dans les magasins il y a plutôt un grand nombre de petites ventes ou bien de grosses ventes. Cela nous permettra de déduire si le consommateur qui se rend dans le magasin privilégie plutôt de gros ou petits achats. Suivant l'un ou l'autre, le type de clientèle et donc les produits proposés ne sont pas les mêmes.

countplot de seaborn permet de visualiser les effectifs des modalités d'une variable catégorielle. En utilisant judicieusement l'attribut hue on peut visualiser pour chaque magasin les occurences des diverses catégories de cat_sales.

  • En se servant de la variable cat_Sales que nous avons créé et countplot de seaborn, observer sous forme d'histogramme les effectifs de chaque catégorie de ventes par magasin.

    Exemple d'utilisation : sns.countplot(x = col, hue = col2, data = df)
In [32]:
 
# Insérez votre code ici
#On peut aller plus loin en utilisant la variable cat_sales que nous avons créé précédemment.
#L'objectif est de voir si dans les magasins il y a plutôt un grand nombre de petites ventes ou bien de grosses ventes.
#Cela nous permettra de déduire si le consommateur qui se rend dans le magasin privilégie plutôt de gros ou petits achats.
#Suivant l'un ou l'autre, le type de clientèle et donc les produits proposés ne sont pas les mêmes.
# Observation sous forme d'histogramme des effectifs de chaque catégorie de ventes par magasin, en se servant de la variable cat_Sales et countplot de sns
plt.figure(figsize = (20, 5))
sns.countplot(x = 'Store', hue = 'cat_Sales', order = agg_store['Store'].values, data = df); # j'ai rajouté moi-même l'ordre pour que ça soit plus clair
plt.title("Effectifs de chaque catégorie de ventes par magasin", fontsize = 16);
plt.legend(loc = "best");
#Sans surprise les 3 magasins leader des ventes ont un plus grand nombre de jour de ventes élevées :
#le magasin 262, un peu plus de 230 jours par an, affiche des ventes entre 16000 et 21372€.
#Si on prend le 897 il n'affiche quasiment que des ventes entre 0 et 5343€.
#Cette information est très liée à la même analyse sur les clients qui sera faite plus loin.
In [18]:
 
plt.figure(figsize = (12,5))
plt.title('Effectifs de chaque catégorie de ventes par magasin',fontsize=18)
sns.countplot(x = 'Store', hue = 'cat_Sales', data = df);
xxxxxxxxxx

Sans surprise les 3 magasins leader des ventes ont un plus grand nombre de jour de ventes élevées : le magasin 262, un peu plus de 230 jours par an, affiche des ventes entre 16000 et 21372€. Si on prend le 897 il n'affiche quasiment que des ventes entre 0 et 5343€. Cette information est très liée à la même analyse sur les clients qui sera faite plus loin.

Tâchons maintenant de regarder la fréquentation des magasins. Pour visualiser rapidement quels sont les magasins qui attirent le plus de clients il est judicieux d'avoir recours à un diagramme circulaire. Il est très facile à comprendre et trouve parfaitement sa place durant des présentations.

Nous devons d'abord trier agg_store par ordre croissant sur le nombre de clients comme pour les ventes. Ensuite nous allons utiliser la fonction pie de pyplot comme dans l'introduction. Rappelons ici qu'il est important de fixer certains attributs de pie :

  • labels : une séquence de chaînes de caractères fournissant les étiquettes pour chaque secteur.
  • autopct : '%1.1f%%'
  • shadow : True
  • startangle : 90
  • textprops : dict(color="w") (pour le style)
  • Afficher sous forme de camembert la proportion des clients captée par chaque magasin.
In [30]:
 
# Insérez votre code ici
#Tâchons maintenant de regarder la fréquentation des magasins.
#Pour visualiser rapidement quels sont les magasins qui attirent le plus de clients il est judicieux d'avoir recours à un diagramme circulaire.
#Il est très facile à comprendre et trouve parfaitement sa place durant des présentations.
# Tri par ordre croissant du DataFrame agg_store sur le nombre de clients
agg_store = agg_store.sort_values(by = 'Customers', ascending = True) # tri par ordre croissant du nombre de clients
# Affichage de la proportion de clients captée par chaque magasin sous forme de camembert
plt.figure(figsize = (20, 12))
list_explode = np.zeros(17).tolist()
list_explode.extend([0.1 ,0.1 ,0.1]) # magasins ordonnées par ordre croissant du nombre de clients, donc les 3 derniers sont les leads, qu'on peut explode
plt.pie(agg_store['Customers'], # dans ce DataFrame agg_store, les modalités de 'Store' sont déja comptées pour chaque store, pas besoin de value_counts()
        labels = agg_store['Store'], # dans ce DataFrame agg_store, les modalités de 'Store' sont déja uniques, pas besoin d'utiliser la méthode unique()
        explode = list_explode,
        autopct = '%1.1f%%',
        startangle = 90,
        shadow = True,
        textprops = dict(color = "w"))
plt.axis('equal')
plt.title("Proportion des clients captée par chaque magasin", fontsize = 16)
plt.legend(loc = "best")
plt.show()
#Les magasins de tête le 262, 251 et le 788 attirent à eux 3, 43.6% des clients sur 2014.
#Ce chiffre explique en grande partie déjà le succès des ventes dans ces 3 magasins.
#A contrario concernant les 3 magasins en queue de peloton, ils attirent moins de 6% des clients sur l'année 2014 ce qui explique des chiffres de vente plus faibles.
#On ne dispose pas des régions européennes dans lesquelles sont implantées chacun des magasins mais elles pourraient expliquer les chiffres de fréquentation.
In [20]:
 
agg_store = agg_store.sort_values(by = 'Customers', ascending = True)
plt.figure(figsize=(10,8))
explode = np.zeros(17).tolist()
explode.extend([0.1,0.1,0.1])
plt.pie(agg_store['Customers'],labels=agg_store['Store'],explode = explode, autopct='%1.1f%%',
        shadow=True, startangle=90,textprops=dict(color="w"))
plt.axis('equal')
plt.title('Proportion des clients captée par chaque magasin',fontsize=18)
plt.legend(loc="best");
xxxxxxxxxx

Les magasins de tête le 262, 251 et le 788 attirent à eux 3, 43.6% des clients sur 2014. Ce chiffre explique en grande partie déjà le succès des ventes dans ces 3 magasins. A contrario concernant les 3 magasins en queue de peloton, ils attirent moins de 6% des clients sur l'année 2014 ce qui explique des chiffres de vente plus faibles. On ne dispose pas des régions européennes dans lesquelles sont implantées chacun des magasins mais elles pourraient expliquer les chiffres de fréquentation.

On peut aller plus loin en regardant les catégories de clients pour chaque magasin.

  • En se servant de la variable cat_Customers que nous avons créé et countplot de seaborn, observer sous forme d'histogramme les effectifs de chaque catégorie de clients par magasin.

    Exemple d'utilisation : sns.countplot(x = col, hue = col2, data = df)
In [33]:
 
# Insérez votre code ici
#On peut aller plus loin en regardant les catégories de clients pour chaque magasin.
# Observation sous forme d'histogramme des effectifs de chaque catégorie de clients par magasin en servant de la variable cat_Customers et countplot de sns
plt.figure(figsize = (20, 5))
sns.countplot(x = 'Store', hue = 'cat_Customers', order = agg_store['Store'].values, data = df); # j'ai rajouté moi-même l'ordre pour que ça soit plus clair
plt.title("Effectifs de chaque catégorie de clients par magasin", fontsize = 16);
plt.legend(loc = "best");
#Ce graphe nous permet tout de suite de voir que pour les 3 magasins de tête ils ont des journées avec des fortes affluences :
#par exemple le magasin 262 compte environ 150 journées avec un nombre de clients entre 3140 et 3925 personnes.
#Si on regarde le magasin 242 il ne compte exclusivement que des journées avec entre 0 et 785 clients.
#La différence se joue donc sur les fortes affluences plus fréquentes dans certains magasins.
#Ces fortes affluences jouent également sur les ventes et expliquent les chiffres trouvés plus haut sur les catégories de vente.
#On peut se demander si ce n'est pas lié au type du magasin.
#Nous n'avons pas accès à la signification des modalités du type de magasin mais on peut penser que certains peuvent être désigner pour un certain
#type de clients, ce qui limiterait ou augmenterait la fréquentation suivant le secteur ciblé.
In [22]:
 
plt.figure(figsize = (12,5))
plt.title('Effectifs de chaque catégorie de clients par magasin',fontsize=18)
sns.countplot(x = 'Store', hue = 'cat_Customers', data = df);
xxxxxxxxxx

Ce graphe nous permet tout de suite de voir que pour les 3 magasins de tête ils ont des journées avec des fortes affluences : par exemple le magasin 262 compte environ 150 journées avec un nombre de clients entre 3140 et 3925 personnes. Si on regarde le magasin 242 il ne compte exclusivement que des journées avec entre 0 et 785 clients. La différence se joue donc sur les fortes affluences plus fréquentes dans certains magasins. Ces fortes affluences jouent également sur les ventes et expliquent les chiffres trouvés plus haut sur les catégories de vente. On peut se demander si ce n'est pas lié au type du magasin. Nous n'avons pas accès à la signification des modalités du type de magasin mais on peut penser que certains peuvent être designer pour un certain type de clients, ce qui limiterait ou augmenterait la fréquentation suivant le secteur ciblé.

Pour également juger de l'efficacité de chaque type de magasin on peut regarder ce que nous allons appeler la performance. Il s'agit simplement de regarder le rapport entre les ventes totales et le nombre de clients par type de magasin. Cela nous donnera une idée du montant dépensé en moyenne par client.

Regardons les ventes, le nombre de clients ainsi que la performance par type de magasin. Concernant les ventes et les clients il vaut mieux privilégier un diagramme circulaire qui sera facile à interpréter. Pour les performances un histogramme est l'idéal : il permet de rapidement voir les écarts entre les types.

  • Créer un DataFrame agg_StoreType qui contient les ventes, clients et la performance pour chaque type.
  • Afficher Sales et Customers en fonction du type de magasin à l'aide d'un diagramme circulaire.
  • Sur la même figure afficher un histogramme avec les performances par type de magasin.
In [47]:
 
# Insérez votre code ici
#Pour également juger de l'efficacité de chaque type de magasin on peut regarder ce que nous allons appeler la performance.
#Il s'agit simplement de regarder le rapport entre les ventes totales et le nombre de clients par type de magasin.
#Cela nous donnera une idée du montant dépensé en moyenne par client.
#Regardons les ventes, le nombre de clients ainsi que la performance par type de magasin.
#Concernant les ventes et les clients il vaut mieux privilégier un diagramme circulaire qui sera facile à interpréter.
#Pour les performances un histogramme est l'idéal : il permet de rapidement voir les écarts entre les types.
# Création d'un DataFrame agg_StoreType qui contient les ventes, clients et la performance pour chaque type
agg_StoreType = df.groupby('StoreType', as_index = False).agg({'Sales':'sum', 'Customers':'sum'})
agg_StoreType['performance'] = agg_StoreType['Sales'] / agg_StoreType['Customers']
# Affichage des 10 premières lignes du DataFrame agg_StoreType
print(agg_StoreType.head(10))
print("\n")
# Préparation de la figure contenant les 3 graphiques mentionnés dans l'énoncé
fig1, ax1 = plt.subplots(1, 3, figsize = (22, 12))
list_explode = [0, 0, 0, 0.1]
hist_color = (0.12156862745098039, 0.47058823529411764, 0.7058823529411765)
### Affichage de 'Sales' et 'Customers' en fonction du type de magasin à l'aide d'un diagramme circulaire ###
# Tri par ordre croissant du DataFrame agg_store sur le nombre de ventes
agg_StoreType = agg_StoreType.sort_values(by = 'Sales', ascending = True)
# Affichage de la proportion de clients captée par chaque type de magasin sous forme de camembert
ax1[0].pie(agg_StoreType['Sales'],
              labels = agg_StoreType['StoreType'],
              explode = list_explode,
              autopct = '%1.1f%%',
              startangle = 90,
              shadow = True,
              textprops = dict(color = "w")) # comme il n'y a qu'une seule ligne dans la grande figure, on ne spécifie qu'un seul indice pour ax1 : ax1[...]
ax1[0].axis('equal')
ax1[0].set_title("Nombre de ventes par type de magasin", fontsize = 16)
ax1[0].legend(loc = "best")
# Tri par ordre croissant du DataFrame agg_store sur le nombre de clients
agg_StoreType = agg_StoreType.sort_values(by = 'Customers', ascending = True)
# Affichage de la proportion de clients captée par chaque type de magasin sous forme de camembert
ax1[1].pie(agg_StoreType['Customers'],
              labels = agg_StoreType['StoreType'].values,
              explode = list_explode,
              autopct = '%1.1f%%',
              startangle = 90,
              shadow = True,
              textprops = dict(color = "w"))
ax1[1].axis('equal')
ax1[1].set_title("Proportion des clients captée par chaque type de magasin", fontsize = 16)
ax1[1].legend(loc = "best")
### Sur la même figure, affichage d'une histogramme avec les performances par type de magasin ###
# tri par ordre croissant du DataFrame agg_store sur la performance
agg_StoreType = agg_StoreType.sort_values(by = 'performance', ascending = True)
# Observation sous forme d'histogramme des performances de chaque type de magasin en servant de la variable performance et barplot de sns
ax1[2] = sns.barplot(x = 'StoreType',
            y = 'performance',
            order = agg_StoreType['StoreType'].values,
            color = hist_color,
            data = agg_StoreType) # la diff avec countplot est la largeur des barres
ax1[2].set_title("Performances par type de magasin", fontsize = 16)
plt.show()
#Le type de magasin présentant les plus grosses ventes présente également le plus grand nombre de clients.
#Cela vient de la forte relation linéaire entre 'Sales' et 'Customers'.
#Cependant si on parle en termes de performance, le type "a" n'arrive qu'en troisième position.
#C'est toujours le même dilemme : faire du volume avec le risque de dégrader la performance contre cibler des clientèles particulières ce qui limite le volume mais améliore la performance.
  StoreType     Sales  Customers  performance
0         a  26812438    3048584     8.795046
1         b   7539709    1245448     6.053813
2         c   5357908     602131     8.898243
3         d   9252882     881943    10.491474


In [24]:
 
agg_StoreType = df.groupby(['StoreType'], as_index = False).sum()
agg_StoreType['performance'] = agg_StoreType['Sales']/agg_StoreType['Customers']
fig1, ax1 = plt.subplots(1,3,figsize=(25,12))
ax1[0].pie(agg_StoreType['Sales'].values,labels=agg_StoreType['StoreType'].values, autopct='%1.1f%%',
            shadow=True, startangle=90,textprops=dict(color="w"))
ax1[0].axis('equal')
ax1[0].set_title('Ventes par type de magasin',fontsize=18)
ax1[0].legend(loc="best");
ax1[1].pie(agg_StoreType['Customers'].values,labels=agg_StoreType['StoreType'].values, autopct='%1.1f%%',
            shadow=True, startangle=90,textprops=dict(color="w"))
ax1[1].axis('equal')
ax1[1].set_title('Nombre de clients par type de magasin',fontsize=18)
ax1[1].legend(loc="best");
ax1[2] = sns.barplot(x = 'StoreType', y = 'performance', data = agg_StoreType, color = color)
ax1[2].set_title('Performance par type de magasin',fontsize=18);
xxxxxxxxxx

Le type a présentant les plus grosses ventes présentent également le plus grand nombre de clients. Cela vient de la forte relation linéaire entre Sales et Customers. Cependant si on parle en termes de performance le type a n'arrive qu'en troisième position. C'est toujours le même dilemme : faire du volume avec le risque de dégrader la performance contre cibler des clientèles particulières ce qui limite le volume mais améliore la performance. Maintenant que nous savons quels sont les magasins moteur en termes de ventes et clients, il s'agit de déterminer leur efficacité. Quand le client pénètre dans le magasin achète-t-il pour un montant élevé de produits ?

Pour cela nous allons nous servir de la notion de performance que nous avons défini juste avant pour les types de magasin. Il s'agira ici, sur le même principe, de faire le rapport entre montant_total_des_ventes et nombre_total_de_clients.

  • Reprendre agg_store et ajouter une variable performance qui est le rapport entre Sales et Customers.
  • Afficher dans un graphe type histogramme la performance de chacun des 20 magasins en utilisant barplot de seaborn.

    Exemple d'utilisation : sns.barplot(x = col1, y = col2, data = df)
In [49]:
 
# Insérez votre code ici
#Maintenant que nous savons quels sont les magasins moteur en termes de ventes et clients, il s'agit de déterminer leur efficacité.
#Quand le client pénètre dans le magasin, achète-t-il pour un montant élevé de produits ?
#Pour cela nous allons nous servir de la notion de performance que nous avons défini juste avant pour les types de magasin.
#Il s'agira ici, sur le même principe, de faire le rapport entre montant_total_des_ventes et nombre_total_de_clients.
# Ajout de la variable 'performance', qui est le rapport entre 'Sales' et 'Customers', au DataFrame agg_store
agg_store['performance'] = agg_store['Sales'] / agg_store['Customers']
# Affichage dans un graphe type histogramme de la performance de chacun des 20 magasins en utilisant barplot de seaborn
plt.figure(figsize = (20, 5))
hist_color = (0.12156862745098039, 0.47058823529411764, 0.7058823529411765)
sns.barplot(x = 'Store', y = 'performance', order = agg_store['Store'].values, color = hist_color, data = agg_store);
plt.title("Performance de chacun des 20 magasins", fontsize = 16);
#Les 3 magasins de tête en termes de ventes que sont le 262, 251 et 788 sont loin d'avoir les meilleurs chiffres de performance.
#Ils font même pire que les 3 derniers en termes de ventes et clients.
#Il faut croire que certes les 3 magasins vendent et attirent le plus, cependant le panier de ventes par client est moins élevé que dans d'autres magasins.
#On tient une piste d'amélioration : les clients viennent et achètent mais pas suffisamment relativement aux autres magasins.
#On peut encore augmenter le potentiel de ventes de ces 3 magasins.
In [26]:
 
agg_store['performance'] = agg_store['Sales']/agg_store['Customers']
plt.figure(figsize = (11,5))
plt.title('Performance par magasin',fontsize=18)
sns.barplot(x = 'Store', y = 'performance', data = agg_store, color = color);
xxxxxxxxxx

Les 3 magasins de tête que sont le 262, 251 et 788 sont loin d'avoir les meilleurs chiffres. Ils font même pire que les 3 derniers en termes de ventes et clients.

Il faut croire que certes les 3 magasins vendent et attirent le plus, cependant le panier de ventes par client est moins élevé que dans d'autres magasins. On tient une piste d'amélioration : les clients viennent et achètent mais pas suffisamment relativement aux autres magasins. On peut encore augmenter le potentiel de ventes de ces 3 magasins.

Un des moyens est de s'intéresser à la stratégie promo mise en place dans les magasins.

  • Afficher agg_store plus haut.
  • Que remarquez-vous sur la stratégie des promos ?
In [50]:
 
# Insérez votre code ici
#Un des moyens d'augmenter le potentiel de ventes de ces 3 magasins est de s'intéresser à la stratégie promo mise en place actuellement dans cess magasins.
# Affichage du DataFrame agg_store
agg_store
# Que remarquez-vous sur la stratégie des promos ?
#Même nombre de promos pour tous les magasins, il n'y a pas de personnalisation au cas par cas en tout cas pas sur le nombre.
Out[50]:
Store Sales Customers Promo CompetitionDistance performance
14 897 1002221 107520 140 [3290.0] 9.321252
3 242 1225796 109741 140 [6880.0] 11.169900
9 708 1141691 113314 140 [11470.0] 10.075463
8 649 1129865 118328 140 [14570.0] 9.548585
1 45 1583098 123528 140 [9710.0] 12.815702
18 1015 1856037 149533 140 [9910.0] 12.412223
17 953 1631708 164322 140 [19830.0] 9.929942
0 1 1433408 168764 140 [1270.0] 8.493565
6 487 2180720 208598 140 [2180.0] 10.454175
2 175 1780588 212533 140 [4130.0] 8.377937
12 774 1669990 215764 140 [640.0] 7.739892
11 745 2285552 216129 140 [17650.0] 10.574944
15 917 2029156 225560 140 [7240.0] 8.996081
16 936 2070564 262242 140 [580.0] 7.895623
7 582 2160389 265826 140 [120.0] 8.127079
10 741 2407231 290543 140 [11900.0] 8.285283
19 1021 2582856 308637 140 [1080.0] 8.368588
13 788 5528074 527903 140 [1530.0] 10.471761
4 251 5724284 743873 140 [340.0] 7.695244
5 262 7539709 1245448 140 [1180.0] 6.053813
In [28]:
 
agg_store
Out[28]:
Store Sales Customers Promo CompetitionDistance performance
14 897 1002221 107520 140 [3290.0] 9.321252
3 242 1225796 109741 140 [6880.0] 11.169900
9 708 1141691 113314 140 [11470.0] 10.075463
8 649 1129865 118328 140 [14570.0] 9.548585
1 45 1583098 123528 140 [9710.0] 12.815702
18 1015 1856037 149533 140 [9910.0] 12.412223
17 953 1631708 164322 140 [19830.0] 9.929942
0 1 1433408 168764 140 [1270.0] 8.493565
6 487 2180720 208598 140 [2180.0] 10.454175
2 175 1780588 212533 140 [4130.0] 8.377937
12 774 1669990 215764 140 [640.0] 7.739892
11 745 2285552 216129 140 [17650.0] 10.574944
15 917 2029156 225560 140 [7240.0] 8.996081
16 936 2070564 262242 140 [580.0] 7.895623
7 582 2160389 265826 140 [120.0] 8.127079
10 741 2407231 290543 140 [11900.0] 8.285283
19 1021 2582856 308637 140 [1080.0] 8.368588
13 788 5528074 527903 140 [1530.0] 10.471761
4 251 5724284 743873 140 [340.0] 7.695244
5 262 7539709 1245448 140 [1180.0] 6.053813
xxxxxxxxxx

Même nombre de promos pour tous les magasins, il n'y a pas de personnalisation au cas par cas en tout cas pas sur le nombre. Regardons par magasin tout de suite si elles ont un impact sur le volume des ventes et le panier individuel d'un client. Comparer la performance pour chaque magasin en période de promotions ou non est idéal. Pour voir une évolution il est judicieux d'utiliser un histogramme dans lequel, pour chaque magasin, nous présenterons la performance en période de réductions ou non.

  • Créer un DataFrame agg_promo qui affiche la moyenne de Sales et de Customers ainsi que la performance de chacun des magasins en période de promotion ou non.
  • Afficher la peformance pour chacun des magasins en période de promotion en vous servant de catplot de seaborn.

    Exemple d'utilisation : sns.catplot(x = col1, y = col2, hue = col3, kind = 'bar', data = df)
In [63]:
 
# Insérez votre code ici
#Regardons par magasin tout de suite si elles ont un impact sur le volume des ventes et le panier individuel d'un client.
#Comparer la performance pour chaque magasin en période de promotions ou non est idéal.
#Pour voir une évolution, il est judicieux d'utiliser un histogramme dans lequel, pour chaque magasin, nous présenterons la performance en période de réductions ou non.
# Création de agg_promo, qui affiche la moyenne de 'Sales' et de 'Customers' ainsi que la performance de chacun des magasins en période de promotions ou non
agg_promo = df.groupby(['Store','Promo'], as_index = False).agg({'Sales':['mean','sum'], 'Customers':['mean','sum']})
agg_promo['performance'] = agg_promo[('Sales','sum')] / agg_promo[('Customers','sum')]
# Affichage de la performance pour chacun des magasins en période de promotion en se servant de catplot de seaborn
sns.catplot(x = 'Store', y = 'performance', hue = 'Promo', kind = 'bar', height = 5, aspect = 3, palette = 'Set1',  order = agg_store['Store'].values, data = agg_promo).fig.suptitle("Performance de chacun des 20 magasins en période de promotions ou non", fontsize = 16);
#Pour la plupart des magasins, les promos ont l'air de fonctionner elles boostent les ventes.
#On voit très nettement une amélioration de la performance en période de promotion.
#Cependant si on prend nos 3 magasins leader, l'amélioration de la performance est moins nette que chez d'autres :
#pour le 251 on passe de 5.9 à 6.2 quand le 45 passe de 12 à 14.
#Peut-être faut-il revoir la stratégie promo pour ces magasins : le choix de la date ou le type de promo proposé.
<Figure size 1440x360 with 0 Axes>
In [62]:
 
agg_promo = df.groupby(['Store','Promo'], as_index = False).agg({'Sales':['mean','sum'],'Customers':['mean','sum']})
agg_promo['performance'] = agg_promo[('Sales','sum')]/agg_promo[('Customers','sum')]
sns.catplot(x = 'Store', y = 'performance', hue = 'Promo', kind = 'bar', height = 5, aspect = 3, palette = 'Set1', data = agg_promo).fig.suptitle('Performance pour chaque magasin en période de promotions ou non',fontsize=18);
xxxxxxxxxx

Pour la plupart des magasins, les promos ont l'air de fonctionner elles boostent les ventes. On voit très nettement une amélioration de la performance en période de promotion. Cependant si on prend nos 3 magasins leader, l'amélioration de la performance est moins nette que chez d'autres : pour le 251 on passe de 5.9 à 6.2 quand le 45 passe de 12 à 14. Peut-être faut-il revoir la stratégie promo pour ces magasins : le choix de la date ou le type de promo proposé.

La question qu'on peut se poser légitimement : des promos oui mais sur quels produits ? Nous disposons de la variable Assortment qui nous renseigne sur le type de panier de produits achetés. Sur le précédent module nous avons remarqué deux types : a = basique et b = élargi.

Nous allons regarder si l'un des deux est plus sensible aux promos.

  • Créer un DataFrame agg_assortment qui affiche la moyenne de Sales de chacun des paniers de produits ainsi que leur performance en période de promotion ou non.
  • Afficher les informations de agg_assortment visuellement à l'aide de catplot de seaborn.

    Exemple d'utilisation : sns.catplot(x = col1, y = col2, hue = col3, kind = 'bar', data = df)
In [66]:
 
# Insérez votre code ici
#La question qu'on peut se poser légitimement : des promos oui mais sur quels produits ?
#Nous disposons de la variable Assortment qui nous renseigne sur le type de panier de produits achetés.
#Sur le précédent module nous avons remarqué deux types : a = basique et b = élargi.
# Création de agg_assortment qui affiche la moyenne de 'Sales' de chacun des paniers de produits ainsi que leur performance en période de promotion ou non
agg_assortment = df.groupby(['Assortment','Promo'], as_index = False).agg({'Sales':['mean','sum'], 'Customers':['mean','sum']})
agg_assortment['performance'] = agg_promo[('Sales','sum')] / agg_promo[('Customers','sum')]
#J'ai mis : agg_assortment[('Sales','sum')] / agg_assortment[('Customers','sum')], à voir si il n'y a pas une faute dans la correction
# Affichage des informations de agg_assortment visuellement à l'aide de catplot de seaborn
sns.catplot(x = 'Assortment', y = ('Sales','mean'), hue = 'Promo', kind = 'bar', height = 5, aspect = 3, palette = 'Set1', data = agg_assortment).fig.suptitle("Moyenne de 'Sales' de chacun des paniers de produits en période de promotion ou non", fontsize = 16);
sns.catplot(x = 'Assortment', y = 'performance', hue = 'Promo', kind = 'bar', height = 5, aspect = 3, palette = 'Set1', data = agg_assortment).fig.suptitle("Performance de chacun des 20 magasins en période de promotions ou non", fontsize = 16);
#Bonne nouvelle, les deux semblent sensibles aux promotions.
#Remarquons que le panier élargi présente une plus forte variation avec un accroissement des ventes plus important en période de promotion.
#L'idée d'être plus agressif sur ce type de panier permettrait d'accroître encore les ventes.
#Cependant cela reste conditionné à la performance des deux paniers.
#En observant le deuxième graphe on voit que la tendance se confirme, l'indice de performance s'améliore plus nettement sur le panier élargi.
In [32]:
 
agg_assortment = df.groupby(['Assortment','Promo'], as_index = False).agg({'Sales':['mean','sum'],'Customers':['mean','sum']})
agg_assortment['performance'] = agg_promo[('Sales','sum')]/agg_promo[('Customers','sum')]
sns.catplot(x = 'Assortment', y = ('Sales','mean'), hue = 'Promo', data = agg_assortment, kind = 'bar').fig.suptitle('Vente',fontsize=15);
sns.catplot(x = 'Assortment', y = 'performance', hue = 'Promo', kind = 'bar', data = agg_assortment).fig.suptitle('Performance',fontsize=15);
xxxxxxxxxx

Bonne nouvelle les deux semblent sensibles aux promotions. Remarquons que le panier élargi présente une plus forte variation avec un accroissement des ventes plus important en période de promotion. L'idée d'être plus agressif sur ce type de panier permettrait d'accroître encore les ventes. Cependant cela reste conditionné à la performance des deux paniers.

En observant le deuxième graphe on voit que la tendance se confirme, l'indice de performance s'améliore plus nettement sur le panier élargi.

Il reste une information à vérifier : l'influence des concurrents sur les ventes. Nous disposons de la variable CompetitionDistance qui renseigne la distance du plus proche magasin concurrent. Il s'agit ici d'identifier si la concurrence est plus un moteur ou un frein pour le développement de nos magasins.

Pour visualiser cette information facilement, on va se servir de nuages de points en pondérant la taille des points par la distance du concurrent le plus proche.

  • Afficher dans 3 graphes différents Sales,Customer et performance de agg_store en fonction de la variable Store en pondérant la taille des points avec CompetitionDistance. Il est conseillé d'utiliser scatterplot de seaborn. L'attribut size permettra de pondérer les points.

    Exemple d'utilisation : scatterplot(x = name_var_1, y = name_var_2, data = df, size = name_var_3, sizes=(120, 4000), legend = False)
In [70]:
 
# Insérez votre code ici
#Il reste une information à vérifier : l'influence des concurrents sur les ventes.
#Nous disposons de la variable 'CompetitionDistance' qui renseigne la distance du plus proche magasin concurrent.
#Il s'agit ici d'identifier si la concurrence est plus un moteur ou un frein pour le développement de nos magasins.
#Pour visualiser cette information facilement, on va se servir de nuages de points en pondérant la taille des points par la distance du concurrent le plus proche.
# Afficher dans 3 graphiques différents 'Sales', 'Customer' et 'performance' de agg_store en fonction de la variable 'Store'
# en pondérant la taille des points avec 'CompetitionDistance', en utilisant scatterplot de seaborn
agg_store['CompetitionDistance'] = agg_store['CompetitionDistance'].astype(np.int64) # pour éviter "AttributeError: Can only use .cat accessor with a 'category' dtype"
agg_store = agg_store.sort_values(by = 'CompetitionDistance', ascending = True)
plt.figure(figsize = (40,25))
plt.subplot(231)
sns.scatterplot(x = 'Store',
                y = 'Sales',
                data = agg_store,
                size = 'CompetitionDistance', # l'attribut size permet de pondérer les points en fonction d'une variable
                sizes=(120, 4000),
                legend = False);
plt.subplot(232)
sns.scatterplot(x = 'Store',
                y = 'Customers',
                data = agg_store,
                size = 'CompetitionDistance',
                sizes=(120, 4000),
                legend = False);
plt.subplot(233)
sns.scatterplot(x = 'Store',
                y = 'performance',
                data = agg_store,
                size = 'CompetitionDistance',
                sizes=(120, 4000),
                legend = False);
#En termes de vente et de clients il semble que les magasins proches de concurrents vendent et attirent le plus.
#On peut penser en toute logique que plusieurs concurrents à côté tirent les ventes vers le haut.
#En termes de performance les magasins plutôt éloignés de leurs concurrents ont de meilleurs scores.
#Si un client vient il achète plus étant donné que la distance des concurrents l’empêche de diversifier.
In [34]:
 
agg_store['CompetitionDistance'] = agg_store['CompetitionDistance'].astype(np.int64)
agg_store = agg_store.sort_values(by = 'CompetitionDistance', ascending = True)
plt.figure(figsize = (40,25))
plt.title('Ventes, clients et performance de chaque magasin selon son concurrent proche')
plt.subplot(231)
sns.scatterplot(x = 'Store', y = 'Sales', data = agg_store, size = 'CompetitionDistance', sizes=(120, 4000), legend = False);
plt.subplot(232)
sns.scatterplot(x = 'Store', y = 'Customers', data = agg_store, size = 'CompetitionDistance', sizes=(120, 4000), legend = False);
plt.subplot(233)
sns.scatterplot(x = 'Store', y = 'performance', data = agg_store, size = 'CompetitionDistance', sizes=(120, 4000), legend = False);
xxxxxxxxxx

Deux approches possibles :

  • En termes de vente et de clients il semble que les magasins proches de concurrents vendent et attirent le plus. On peut penser en toute logique que plusieurs concurrents à côté tirent les ventes vers le haut.
  • En termes de performance les magasins plutôt éloignés de leurs concurrents ont de meilleurs scores. Si un client vient il achète plus étant donné que la distance des concurrents l’empêche de diversifier.

Enfin il est important de regarder l'impact des jours fériés et vacances scolaires par magasin. Comme pour les promotions nous allons regarder l'indice de performance en période de vacances scolaires ou non. Il est recommandé d'utiliser un histogramme pour rapidement saisir l'information.

  • En utilisant la variable SchoolHoliday stocker dans un tableau nommé agg_conges les ventes, le nombre de clients et la performance de chaque magasin en période de vacances scolaires ou non.
  • Afficher la peformance pour chacun des magasins en période de vacances scolaires en vous servant de catplot de seaborn.
In [79]:
 
# Insérez votre code ici
#Enfin il est important de regarder l'impact des jours fériés et vacances scolaires par magasin.
#Comme pour les promotions nous allons regarder l'indice de performance en période de vacances scolaires ou non.
#Il est recommandé d'utiliser un histogramme pour rapidement saisir l'information.
# Tri des magasins par ordre croissant des ventes
agg_store = agg_store.sort_values(by = 'Sales', ascending = True) 
# Stockage dans un tableau nommé agg_conges des ventes, du nombre de clients et de la performande de chaque en période de vacances scolaires ou non
agg_conges = df.groupby(['Store', 'SchoolHoliday'], as_index = False).agg({'Sales':'sum', 'Customers':'sum'})
agg_conges['performance'] = agg_conges['Sales'] / agg_conges['Customers']
# Affichage de la performance pour chacun des magasins en période de vacances scolaires ou non, en se servant de catplot de seaborn
sns.catplot(x = 'Store', y = 'performance', hue = 'SchoolHoliday', kind = 'bar', height = 5, aspect = 3, order = agg_store['Store'].values, palette = 'Set1', data = agg_conges).fig.suptitle("Peformance de chaque magasin en période de vacances scolaires ou non", fontsize = 16);
#Il est clair d'après le graphe qu'au regard de la performance des magasins, les vacances scolaires n'ont pas vraiment d'incidence sur les ventes des magasins.
#Qu'en est-il des jours fériés ?
In [36]:
 
agg_conges = df.groupby(['Store','SchoolHoliday'],as_index = False).agg({'Sales':'sum','Customers':'sum'})
agg_conges['performance'] = agg_conges['Sales']/agg_conges['Customers']
sns.catplot(x = 'Store', y = 'performance', hue = 'SchoolHoliday', kind = 'bar', height = 5, aspect = 3, palette = 'Set1', data = agg_conges).fig.suptitle('Performance pour chaque magasin en période de vacances scolaires ou non',fontsize=18);
xxxxxxxxxx

Il est clair qu'au regard de la performance des magasins, les vacances scolaires n'ont pas vraiment d'incidence sur les ventes des magasins. Qu'en est-il des jours fériés ?

  • En utilisant StateHoliday, regarder les ventes par magasin pendant les jours fériés
In [83]:
 
# Insérez votre code ici
### En utilisant StateHoliday, regarder les ventes par magasin pendant les jours fériés ###
agg_feries = df.groupby(['Store', 'StateHoliday'], as_index = False).agg({'Sales':'sum', 'Customers':'sum'})
agg_feries['performance'] = agg_feries['Sales'] / agg_feries['Customers']
# Affichage des ventes par performance pour chacun des magasins en période de jours fériés ou non, en se servant de catplot de seaborn
sns.catplot(x = 'Store', y = 'performance', hue = 'StateHoliday', kind = 'bar', height = 5, aspect = 3, order = agg_store['Store'].values, palette = 'Set1', data = agg_feries).fig.suptitle("Peformance de chaque magasin en période jours fériés ou non", fontsize = 16);
# Correction :
liste_jours_feries = ['b','c']
agg_ferie = df.loc[(df["StateHoliday"] == 'a') & (df["Sales"] > 0)][['Store','Sales']]
for i in liste_jours_feries :
    tmp = df.loc[(df["StateHoliday"] == i) & (df["Sales"] > 0)][['Store','Sales']]
    agg_ferie = pd.concat([agg_ferie, tmp])
    
agg_ferie = agg_ferie.groupby('Store',as_index = False).sum()
agg_ferie
#4 magasins effectuent des ventes pendant les jours fériés y compris le 262 : un des magasins en tête des ventes.
Out[83]:
Store Sales
0 262 287930
1 582 2146
2 708 12951
3 745 8197
In [61]:
 
liste_jours_feries = ['b','c']
agg_ferie = df.loc[(df["StateHoliday"] == 'a') & (df["Sales"] > 0)][['Store','Sales']]
for i in liste_jours_feries :
    tmp = df.loc[(df["StateHoliday"] == i) & (df["Sales"] > 0)][['Store','Sales']]
    agg_ferie = pd.concat([agg_ferie, tmp])
    
agg_ferie = agg_ferie.groupby('Store',as_index = False).sum()
agg_ferie
Out[61]:
Store Sales
0 262 287930
1 582 2146
2 708 12951
3 745 8197
xxxxxxxxxx

4 magasins effectuent des ventes pendant les jours fériés y compris le 262 un des magasins en tête des ventes.

Conclusion

En faisant appel aux librairies matplotlib et seaborn nous avons mis des outils de DataViz' au service de l'analyse de notre jeu de données. Nous avons pu déterminer les magasins leader des ventes et de la fréquentation client, juger de l'efficacité de chacun au travers d'un indicateur de performance simple, quantifier l'impact des promotions sur les ventes des magasins, identifier les paniers de produits les plus sensibles aux promotions et enfin l'impact de la concurrence sur les ventes et la performance des magasins.

Unvalidate